import os
import serial
import serial.tools.list_ports
import json
from flask import Flask, request, jsonify, render_template
from collections import deque
import subprocess
import threading

app = Flask(__name__)
serial_port = None
serial_data_queue = deque(maxlen=1024)
udp_output_queue = deque(maxlen=10)
cache_file = './serial_server_cache.json'
endlines = ["", "\r", "\n", "\r\n"]
udp_process = None
udp_thread = None

def load_cache():
    if os.path.exists(cache_file):
        with open(cache_file, 'r') as f:
            return json.load(f)
    return {}

def save_cache(data):
    with open(cache_file, 'w') as f:
        json.dump(data, f)

def read_udp_output(process):
    while True:
        output = process.stdout.readline()
        if output:
            udp_output_queue.append(output.decode().strip())
        error = process.stderr.readline()
        if error:
            udp_output_queue.append(f"ERROR: {error.decode().strip()}")
        if process.poll() is not None:
            break

@app.route('/')
def index():
    return render_template('serial_terminal.html')

@app.route('/scan_serial_ports', methods=['GET'])
def scan_serial_ports():
    ports = [port.device for port in serial.tools.list_ports.comports()]
    return jsonify(ports)

@app.route('/open_serial_port', methods=['POST'])
def open_serial_port():
    global serial_port
    data = request.json
    port = data.get('port')
    baud_rate = data.get('baudRate')
    try:
        serial_port = serial.Serial(port, baud_rate, timeout=1)
        cache = load_cache()
        cache['last_port'] = port
        cache['last_baud_rate'] = baud_rate
        save_cache(cache)
        return jsonify({"status": "success"})
    except Exception as e:
        print(f"error: {e}")
        return jsonify({"status": "error", "message": str(e)})

@app.route('/close_serial_port', methods=['POST'])
def close_serial_port():
    global serial_port
    if serial_port and serial_port.is_open:
        serial_port.close()
        serial_port = None
    return jsonify({"status": "success"})

@app.route('/send_data', methods=['POST'])
def send_data():
    global serial_port
    data = request.json.get('data')
    newline_index = request.json.get('newline')
    try:
        if serial_port and serial_port.is_open:
            newline = endlines[int(newline_index)]
            serial_port.write((data + newline).encode())
            return jsonify({"status": "success"})
        else:
            return jsonify({"status": "error", "message": "串口未打开"})
    except Exception as e:
        print(f"error: {e}")
        return jsonify({"status": "error", "message": str(e)})

@app.route('/read_serial_data', methods=['GET'])
def read_serial_data():
    global serial_port, serial_data_queue
    try:
        if serial_port and serial_port.is_open:
            lines = serial_port.readlines()
            data = ''.join([line.decode() for line in lines])
            if data:
                serial_data_queue.clear()
                serial_data_queue.append(data)
                return jsonify({"status": "success", "data": data})
            else:
                return jsonify({"status": "success", "data": ""})
        else:
            return jsonify({"status": "error", "message": "串口未打开"})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})

@app.route('/get_last_settings', methods=['GET'])
def get_last_settings():
    cache = load_cache()
    return jsonify(cache)

@app.route('/get_serial_status', methods=['GET'])
def get_serial_status():
    global serial_port
    if serial_port and serial_port.is_open:
        return jsonify({"status": "open"})
    else:
        return jsonify({"status": "closed"})

@app.route('/start_udp_to_serial', methods=['POST'])
def start_udp_to_serial():
    global udp_process, udp_thread
    data = request.json
    udp_port = data.get('udpPort')
    serial_port = data.get('serialPort')
    baud_rate = data.get('baudRate')
    if udp_process is None:
        try:
            udp_process = subprocess.Popen(
                ['/home/umeko/ws/ws_venv/bin/python3', '/home/umeko/ws/mqtt_cam_server/server/robotserver.py', '--udp_port', str(udp_port), '--serial_port', serial_port, '--baud_rate', str(baud_rate)],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
            udp_thread = threading.Thread(target=read_udp_output, args=(udp_process,))
            udp_thread.start()
            cache = load_cache()
            cache['udp_port'] = udp_port
            cache['udp_serial_port'] = serial_port
            cache['udp_baud_rate'] = baud_rate
            save_cache(cache)
            return jsonify({"status": "success"})
        except Exception as e:
            print(f"error: {e}")
            return jsonify({"status": "error", "message": str(e)})
    else:
        return jsonify({"status": "error", "message": "UDP转串口已在运行"})

@app.route('/stop_udp_to_serial', methods=['POST'])
def stop_udp_to_serial():
    global udp_process
    if udp_process:
        udp_process.terminate()
        udp_process = None
        return jsonify({"status": "success"})
    else:
        return jsonify({"status": "error", "message": "UDP转串口未在运行"})

@app.route('/get_udp_status', methods=['GET'])
def get_udp_status():
    global udp_process, udp_output_queue
    if udp_process and udp_process.poll() is None:
        messages = list(udp_output_queue)
        return jsonify({"status": "success", "messages": messages})
    else:
        messages = list(udp_output_queue)
        return jsonify({"status": "error", "message": "UDP转串口未运行", "messages": messages})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001)
